﻿using IOP.Codec.OpenType;
using IOP.OpenType.Tables;
using System;
using System.Collections.Generic;
using System.IO;
using System.Numerics;
using System.Text.Json;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
using System.Drawing;

namespace IOP.OpenType.Rasterizer
{
    /// <summary>
    /// TTF光栅化器
    /// </summary>
    public class OTFRasterizer : IDisposable
    {
        /// <summary>
        /// 解码器
        /// </summary>
        private OTFDecoder _Decoder { get; set; }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="info"></param>
        public OTFRasterizer(FileInfo info)
        {
            _Decoder = new OTFDecoder(info.FullName);
        }

        /// <summary>
        /// 创建有向距离场字符图片
        /// </summary>
        /// <param name="createOption"></param>
        /// <returns></returns>
        public async Task CreateSDF(RasterizationOption createOption)
        {
            var charsTable = CreateCharsTable(createOption);
            var head = await _Decoder.ReadHeadTable();
            uint u = createOption.ImageWidth / createOption.UnitsPerEm;
            uint dPerEm = createOption.DesignPerEm;
            uint uPerEm = createOption.UnitsPerEm;
            using CountdownEvent cEvent = new CountdownEvent(64);
            for (int m = 0; m < charsTable.Count; m++)
            {
                byte[,] finalBytes = new byte[createOption.ImageWidth, createOption.ImageWidth];
                char[][] chars = charsTable[m];
                var rows = chars.Length;
                int rowIndex = -1;
                cEvent.Reset(rows);
                for (int i = 0; i < rows; i++)
                {
                    _ = Task.Factory.StartNew(async () =>
                    {
                        try
                        {
                            var rIndex = Interlocked.Increment(ref rowIndex);
                            var rowChars = chars[rIndex];
                            Glyph[] glyphs = await _Decoder.ReadGlyphs(rowChars, rowChars.Length);
                            uint dPerEm = createOption.DesignPerEm;
                            uint uPerEm = createOption.UnitsPerEm;
                            float t = dPerEm / uPerEm;
                            byte[,] localBitmap = new byte[dPerEm, dPerEm];
                            SDFGrid grid1 = new SDFGrid() { Distance = new Point[dPerEm, dPerEm] };
                            SDFGrid grid2 = new SDFGrid() { Distance = new Point[dPerEm, dPerEm] };
                            byte[,] outBitmap = new byte[uPerEm, uPerEm];
                            for (int m = 0; m < glyphs.Length; m++)
                            {
                                Rasterization(localBitmap, head, glyphs[m]);
                                RasterizationCommand.EightSsedt(localBitmap, grid1, grid2);
                                for (int y = 0; y < uPerEm; y++)
                                {
                                    for (int x = 0; x < uPerEm; x++)
                                    {
                                        outBitmap[y, x] = localBitmap[(int)(y * t), (int)(x * t)];
                                    }
                                }
                                CopySourceToOutput(outBitmap, finalBytes, uPerEm, rIndex, m);
                                Array.Clear(outBitmap, 0, outBitmap.Length);
                                Array.Clear(localBitmap, 0, localBitmap.Length);
                            }
                        }
                        finally
                        {
                            cEvent.Signal();
                        }
                    });
                }
                cEvent.Wait();
                Save(finalBytes, createOption, m);
            }
        }
        /// <summary>
        /// 创建以GlyphId为序列的SDF图片
        /// </summary>
        /// <param name="createOption"></param>
        /// <returns></returns>
        public async Task CreateSDFFromGlyphId(RasterizationOption createOption)
        {
            var gidTable = await CreateGlyphIdsTable(createOption);
            var head = await _Decoder.ReadHeadTable();
            uint u = createOption.ImageWidth / createOption.UnitsPerEm;
            uint dPerEm = createOption.DesignPerEm;
            uint uPerEm = createOption.UnitsPerEm;
            using CountdownEvent cEvent = new CountdownEvent(64);
            for (int m = 0; m < gidTable.Count; m++)
            {
                byte[,] finalBytes = new byte[createOption.ImageWidth, createOption.ImageWidth];
                uint[][] gIds = gidTable[m];
                var rows = gIds.Length;
                int rowIndex = -1;
                cEvent.Reset(rows);
                for (int i = 0; i < rows; i++)
                {
                    _ = Task.Factory.StartNew(async () =>
                    {
                        try
                        {
                            var rIndex = Interlocked.Increment(ref rowIndex);
                            var rowGId = gIds[rIndex];
                            Glyph[] glyphs = await _Decoder.ReadGlyphs(rowGId, rowGId.Length);
                            uint dPerEm = createOption.DesignPerEm;
                            uint uPerEm = createOption.UnitsPerEm;
                            byte[,] localBitmap = new byte[dPerEm, dPerEm];
                            byte[,] outBitmap = new byte[uPerEm, uPerEm];
                            SDFGrid grid1 = new SDFGrid() { Distance = new Point[dPerEm, dPerEm] };
                            SDFGrid grid2 = new SDFGrid() { Distance = new Point[dPerEm, dPerEm] };
                            uint t = dPerEm / uPerEm;
                            for (int m = 0; m < glyphs.Length; m++)
                            {
                                Rasterization(localBitmap, head, glyphs[m]);
                                RasterizationCommand.EightSsedt(localBitmap, grid1, grid2);
                                for (int y = 0; y < uPerEm; y++)
                                {
                                    for (int x = 0; x < uPerEm; x++)
                                    {
                                        outBitmap[y, x] = localBitmap[(int)(y * t), (int)(x * t)];
                                    }
                                }
                                CopySourceToOutput(outBitmap, finalBytes, uPerEm, rIndex, m);
                                Array.Clear(outBitmap, 0, outBitmap.Length);
                                Array.Clear(localBitmap, 0, localBitmap.Length);
                            }
                        }
                        finally
                        {
                            cEvent.Signal();
                        }
                    });
                }
                cEvent.Wait();
                Save(finalBytes, createOption, m);
            }
        }

        /// <summary>
        /// 创建字符表
        /// </summary>
        /// <param name="createOption"></param>
        /// <returns></returns>
        public List<char[][]> CreateCharsTable(RasterizationOption createOption)
        {
            if (createOption == null) throw new ArgumentNullException(nameof(createOption));
            if (createOption.MaxChar < createOption.MinChar) throw new IndexOutOfRangeException("The max char is litter than min char");
            uint u = createOption.ImageWidth / createOption.UnitsPerEm;
            char min = createOption.MinChar;
            char max = createOption.MaxChar;
            uint range = (uint)(max - min) + 1;
            uint singleLength = u * u;
            uint page = range / singleLength;
            page = range % singleLength == 0 ? page : page + 1;
            uint charIndex = createOption.MinChar;
            uint already = range;
            List<char[][]> charsTable = new List<char[][]>();
            for(int i = 0; i < page; i++)
            {
                uint diff = already;
                diff = diff < singleLength ? diff : singleLength;
                uint column = diff / u;
                column = diff % u == 0 ? column : column + 1;
                char[][] pageTable = new char[column][];
                charsTable.Add(pageTable);
                already -= diff;
                uint localAlready = diff;
                for(int j = 0; j < column; j++)
                {
                    uint localDiff = localAlready;
                    localDiff = localDiff < u ? localDiff : u;
                    char[] row = new char[localDiff];
                    pageTable[j] = row;
                    for(uint m = 0; m < localDiff; m++)
                    {
                        row[m] = (char)charIndex++;
                    }
                    localAlready -= localDiff;
                }
            }
            return charsTable;
        }
        /// <summary>
        /// 创建Gid表
        /// </summary>
        /// <param name="createOption"></param>
        /// <returns></returns>
        public async Task<List<uint[][]>> CreateGlyphIdsTable(RasterizationOption createOption)
        {
            var gDiff = createOption.MaxGlyphId - createOption.MinGlyphId;
            if (gDiff < 0) throw new IndexOutOfRangeException("MaxGlyphId must be greater than MinGlyphId");
            var maxp = await _Decoder.ReadMaxpTable();
            var max = createOption.MaxGlyphId + 1 >= maxp.NumGlyphs ? maxp.NumGlyphs : createOption.MaxGlyphId + 1;
            var min = createOption.MinGlyphId;
            uint u = createOption.ImageWidth / createOption.UnitsPerEm;
            uint range = max - min;
            uint singleLength = u * u;
            uint page = range / singleLength;
            page = range % singleLength == 0 ? page : page + 1;
            uint already = range;
            uint index = min;
            List<uint[][]> gIdsTable = new List<uint[][]>();
            for (int i = 0; i < page; i++)
            {
                uint diff = already;
                diff = diff < singleLength ? diff : singleLength;
                uint column = diff / u;
                column = diff % u == 0 ? column : column + 1;
                uint[][] pageTable = new uint[column][];
                gIdsTable.Add(pageTable);
                already -= diff;
                uint localAlready = diff;
                for (int j = 0; j < column; j++)
                {
                    uint localDiff = localAlready;
                    localDiff = localDiff < u ? localDiff : u;
                    uint[] row = new uint[localDiff];
                    pageTable[j] = row;
                    for (uint m = 0; m < localDiff; m++) row[m] = index++;
                    localAlready -= localDiff;
                }
            }
            return gIdsTable;
        }

        /// <summary>
        /// 光栅化单个字符
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Rasterization(byte[,] bitmap, HeadTable head, Glyph glyph)
        {
            if (bitmap == null) throw new ArgumentNullException(nameof(bitmap));
            int designPerEm = bitmap.GetLength(0);
            Curve[] curves = glyph.Curves;
            Curve[] trueCurves = new Curve[curves.Length];
            int cLength = curves.Length;
            int em = head.UnitsPerEm;
            float u = (float)designPerEm / em;
            Matrix3x2 mat = Matrix3x2.CreateScale(u);
            GlyphHeader dHeader = glyph.Header;
            int uxMax = (int)(dHeader.XMax * u);
            int uxMin = (int)(dHeader.XMin * u);
            int uyMax = (int)(dHeader.YMax * u);
            int uyMin = (int)(dHeader.YMin * u);
            int width = uxMax - uxMin;
            int height = uyMax - uyMin;
            int xDelta = (designPerEm - width) / 2;
            int yDelta = (designPerEm - height) / 2;
            Vector2[] seeds = new Vector2[cLength];
            for (int i = 0; i < cLength; i++)
            {
                Curve curve = curves[i];
                var seg = curve.CurveSegment;
                Vector2[][] truePoints = new Vector2[seg.Length][];
                for (int j = 0; j < seg.Length; j++)
                {
                    Vector2[] p = seg[j];
                    if (p.Length == 2)
                    {
                        var p0 = TransformPoint(p[0], designPerEm, uxMin, uyMin, xDelta, yDelta, mat);
                        var p1 = TransformPoint(p[1], designPerEm, uxMin, uyMin, xDelta, yDelta, mat);
                        RasterizationCommand.PlotLine(bitmap, (int)p0.X, (int)p0.Y, (int)p1.X, (int)p1.Y, 255);
                        truePoints[j] = new Vector2[] { p0, p1 };
                    }
                    else
                    {
                        var p0 = TransformPoint(p[0], designPerEm, uxMin, uyMin, xDelta, yDelta, mat);
                        var p1 = TransformPoint(p[1], designPerEm, uxMin, uyMin, xDelta, yDelta, mat);
                        var p2 = TransformPoint(p[2], designPerEm, uxMin, uyMin, xDelta, yDelta, mat);
                        RasterizationCommand.PlotQuadBezier(bitmap, (int)p0.X, (int)p0.Y, (int)p1.X, (int)p1.Y, (int)p2.X, (int)p2.Y, 255);
                        truePoints[j] = new Vector2[] { p0, p1, p2 };
                    }
                }
                trueCurves[i] = new Curve { CurveSegment = truePoints };
            }
            for (int i = 0; i < cLength; i++)
            {
                Vector2 seed = FindSeed(bitmap, trueCurves[i].CurveSegment, u);
                seeds[i] = seed;
            }
            for (int i = 0; i < cLength; i++)
            {
                RasterizationCommand.SeedScanFill(bitmap, seeds[i], 255);
            }
        }
        /// <summary>
        /// 光栅化单个字符
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="c"></param>
        /// <param name="platformID"></param>
        /// <param name="encodingID"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public async Task Rasterization(byte[,] bitmap, char c, ushort platformID = 3, ushort encodingID = 1)
        {
            var head = await _Decoder.ReadHeadTable();
            if (bitmap == null) throw new ArgumentNullException(nameof(bitmap));
            Glyph glyph = await _Decoder.ReadGlyph(c, platformID, encodingID);
            Rasterization(bitmap, head, glyph);
        }
        /// <summary>
        /// 导出光栅化文字数据
        /// </summary>
        /// <param name="chars">文字数组</param>
        /// <param name="rasterizeBoxLength">光栅化网格盒长宽</param>
        /// <param name="platformID">平台ID</param>
        /// <param name="encodingID">编码ID</param>
        /// <returns></returns>
        public async Task<List<RasterizationFont>> ExportRasterizationFont(char[] chars, uint rasterizeBoxLength, ushort platformID = 3, ushort encodingID = 1)
        {
            if (chars == null) throw new ArgumentNullException(nameof(chars));
            if (rasterizeBoxLength <= 0) throw new InvalidOperationException("rasterizeBox is too small");
            if (chars.Length <= 0) return new List<RasterizationFont>();
            var head = await _Decoder.ReadHeadTable();
            var hhea = await _Decoder.ReadHheaTable();
            var hxtm = await _Decoder.ReadHmtxTable();
            var cmap = await _Decoder.ReadCmapTable();
            float em = head.UnitsPerEm;
            float lineHeight = hhea.Ascender - hhea.Descender + hhea.LineGap;
            float perLineHeight = lineHeight / em;
            float ascender = perLineHeight;
            float descender = (-hhea.Descender + hhea.LineGap) / em;
            float lineGap = hhea.LineGap / em;
            List<RasterizationFont> result = new List<RasterizationFont>();
            Glyph[] glyphs = await _Decoder.ReadGlyphs(chars, platformID, encodingID);
            for (int i = 0; i < chars.Length; i++)
            {
                RasterizationFont info = new RasterizationFont();
                var gid = cmap.GetGlyphId(platformID, encodingID, chars[i]);
                LongHorMetric horMetric = hxtm.GetHorMetric(gid);
                GlyphHeader gHeader = glyphs[i].Header;
                var height = gHeader.YMax - gHeader.YMin;
                var width = gHeader.XMax - gHeader.XMin;
                info.Ascender = ascender;
                info.Descender = descender;
                info.LingGap = lineGap;
                info.LineHeight = perLineHeight;
                info.AdvanceWidth = horMetric.AdvanceWidth / em;
                info.LeftSideBearings = horMetric.LeftSideBearing / em;
                info.RightSideBearings = (horMetric.AdvanceWidth - horMetric.LeftSideBearing - width) / em;
                info.GId = gid;
                info.Character = new string(chars[i], 1);
                info.UnitsPerEm = em;
                info.RasterizationPerEm = rasterizeBoxLength;
                float xMin = gHeader.XMin / em;
                float xMax = gHeader.XMax / em;
                float yMin = gHeader.YMin / em + descender;
                float yMax = gHeader.YMax / em + descender;
                Bounding bounding = new Bounding(xMin, xMax, yMin, yMax);
                info.Bounding = bounding;
                float xDelta = (em - width) / 2;
                float yDelta = (em - height) / 2;
                Vector2 bottom = TransformPoint(gHeader.XMin, gHeader.YMax, em, gHeader.XMin, gHeader.YMin, xDelta, yDelta);
                Vector2 top = TransformPoint(gHeader.XMax, gHeader.YMin, em, gHeader.XMin, gHeader.YMin, xDelta, yDelta);
                Bounding uv = new Bounding(bottom.X, top.X, bottom.Y, top.Y);
                info.UVBounding = uv;
                byte[,] data = new byte[rasterizeBoxLength, rasterizeBoxLength];
                Rasterization(data, head, glyphs[i]);
                info.RasterizationData = data;
                result.Add(info);
            }
            return result;
        }

        /// <summary>
        /// 导出文字度量
        /// </summary>
        /// <param name="createOption"></param>
        /// <returns></returns>
        public async Task<FontMetrics> ExportMetrics(RasterizationOption createOption)
        {
            if (createOption == null) throw new ArgumentNullException(nameof(createOption));
            var head = await _Decoder.ReadHeadTable();
            var maxp = await _Decoder.ReadMaxpTable();
            var hhea = await _Decoder.ReadHheaTable();
            var hxtm = await _Decoder.ReadHmtxTable();
            float em = head.UnitsPerEm;
            float lineHeight = hhea.Ascender - hhea.Descender + hhea.LineGap;
            float perLineHeight = lineHeight / em;
            float ascender = perLineHeight;
            float descender = (-hhea.Descender + hhea.LineGap) / em;
            float lineGap = hhea.LineGap / em;
            List<Metrics> metricsArray = new List<Metrics>(maxp.NumGlyphs);
            FontMetrics fontMetrics = new FontMetrics()
            {
                Ascender = ascender,
                Descender = descender,
                LingGap = lineGap,
                LineHeight = perLineHeight,
            };
            fontMetrics.Metrics = metricsArray;
            for (uint gid = 0; gid < maxp.NumGlyphs; gid++)
            {
                LongHorMetric horMetric = hxtm.GetHorMetric(gid);
                GlyphDescription g = await _Decoder.ReadGlyfData(gid);
                GlyphHeader gHeader = g.Header;
                var height = gHeader.YMax - gHeader.YMin;
                var width = gHeader.XMax - gHeader.XMin;
                Metrics metrics = new Metrics
                {
                    AdvanceWidth = horMetric.AdvanceWidth / em,
                    LeftSideBearings = horMetric.LeftSideBearing / em,
                    RightSideBearings = (horMetric.AdvanceWidth - horMetric.LeftSideBearing - width) / em,
                    GId = gid
                };
                float xMin = gHeader.XMin / em;
                float xMax = gHeader.XMax / em;
                float yMin = gHeader.YMin / em + descender;
                float yMax = gHeader.YMax / em + descender;
                Bounding bounding = new Bounding(xMin, xMax, yMin, yMax);
                metrics.Bounding = bounding;
                float xDelta = (em - width) / 2;
                float yDelta = (em - height) / 2;
                Vector2 bottom = TransformPoint(gHeader.XMin, gHeader.YMax, em, gHeader.XMin, gHeader.YMin, xDelta, yDelta);
                Vector2 top = TransformPoint(gHeader.XMax, gHeader.YMin, em, gHeader.XMin, gHeader.YMin, xDelta, yDelta);
                Bounding uv = new Bounding(bottom.X, top.X, bottom.Y, top.Y);
                metrics.UVBounding = uv;
                metricsArray.Add(metrics);
            }
            return fontMetrics;
        }
        /// <summary>
        /// 导出字形映射
        /// </summary>
        /// <param name="createOption"></param>
        /// <returns></returns>
        public async Task<List<GlyphMapping>> ExportGlyphMapping(RasterizationOption createOption)
        {
            if (createOption == null) throw new ArgumentNullException(nameof(createOption));
            if (createOption.MaxChar < createOption.MinChar) throw new IndexOutOfRangeException("The max char is litter than min char");
            var length = createOption.MaxChar - createOption.MinChar + 1;
            List<GlyphMapping> glyphs = new List<GlyphMapping>(length);
            var camp = await _Decoder.ReadCmapTable();
            ushort pId = createOption.PlatformID;
            ushort eId = createOption.EncodingID;
            for(char c = createOption.MinChar; c < createOption.MaxChar; c++)
            {
                uint g = camp.GetGlyphId(pId, eId, c);
                GlyphMapping mapping = new GlyphMapping() { Character = new string(c, 1), GlyphId = g };
                glyphs.Add(mapping);
            }
            return glyphs;

        }
        /// <summary>
        /// 导出文字度量至Json文件
        /// </summary>
        /// <param name="createOption"></param>
        /// <returns></returns>
        public async Task ExportMetricsToJson(RasterizationOption createOption)
        {
            if (string.IsNullOrEmpty(createOption.Path)) throw new ArgumentNullException(nameof(Path));
            if (string.IsNullOrEmpty(createOption.Name)) createOption.Name = Guid.NewGuid().ToString("N");
            FontMetrics metrics = await ExportMetrics(createOption);
            string json = JsonSerializer.Serialize(metrics);
            using FileStream file = new FileStream(Path.Combine(createOption.Path, $"{createOption.Name}.json"), FileMode.OpenOrCreate);
            using StreamWriter writer = new StreamWriter(file);
            await writer.WriteAsync(json);
        }
        /// <summary>
        /// 导出字形映射至Json文件
        /// </summary>
        /// <param name="createOption"></param>
        /// <returns></returns>
        public async Task ExportGlyphMappingToJson(RasterizationOption createOption)
        {
            if (string.IsNullOrEmpty(createOption.Path)) throw new ArgumentNullException(nameof(Path));
            if (string.IsNullOrEmpty(createOption.Name)) createOption.Name = Guid.NewGuid().ToString("N");
            List<GlyphMapping> mappings = await ExportGlyphMapping(createOption);
            string json = JsonSerializer.Serialize(mappings);
            using FileStream file = new FileStream(Path.Combine(createOption.Path, $"{createOption.Name}.json"), FileMode.OpenOrCreate);
            using StreamWriter writer = new StreamWriter(file);
            await writer.WriteAsync(json);
        }

        /// <summary>
        /// 保存
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="createOption"></param>
        /// <param name="arrayIndex"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Save(byte[,] bitmap, RasterizationOption createOption, int arrayIndex)
        {
            var name = createOption.Name;
            var path = createOption.Path;
            if (string.IsNullOrEmpty(name)) name = Guid.NewGuid().ToString("N");
            else name += arrayIndex;
            switch (createOption.ImageFormat)
            {
                case ImageFormat.Bmp:
                    name += ".bmp";
                    path = Path.Combine(path, name);
                    SaveToBitmap(bitmap, System.Drawing.Imaging.ImageFormat.Bmp, path);
                    break;
                case ImageFormat.Jpeg:
                    name += ".jpg";
                    path = Path.Combine(path, name);
                    SaveToBitmap(bitmap, System.Drawing.Imaging.ImageFormat.Jpeg, path);
                    break;
                case ImageFormat.Png:
                    name += ".png";
                    path = Path.Combine(path, name);
                    SaveToBitmap(bitmap, System.Drawing.Imaging.ImageFormat.Png, path);
                    break;
                case ImageFormat.Bntexa:
                    throw new NotSupportedException();
                default:
                    throw new NotSupportedException();
            }
        }

        /// <summary>
        /// 销毁资源
        /// </summary>
        public void Dispose()
        {
            _Decoder.Dispose();
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private Vector2 FindSeed(byte[,] bitmap, Vector2[][] curves, float u)
        {
            u /= 2;
            int uu;
            if (u <= 1) uu = 1; //当缩放系数小于1或者接近1时可能会导致查到错误的种子
            else uu = (int)u;
            int[] controlX1 = new int[] { uu, uu, -uu, -uu };
            int[] controlY1 = new int[] { -uu, uu, uu, -uu };
            int[] controlX2 = new int[] { 0, 0 };
            int[] controlY2 = new int[] { uu, -uu };
            for (int i = 0; i < curves.Length; i++)
            {
                Vector2[] vv = curves[i];
                for (int n = 0; n < vv.Length; n++)
                {
                    Vector2 v = vv[n];
                    for (int m = 0; m < controlX1.Length; m++)
                    {
                        Vector2 local = new Vector2(v.X + controlX1[m], v.Y + controlY1[m]);
                        if (InsideTest(bitmap, local, 255)) return local;
                    }
                }
            }
            for (int i = 0; i < curves.Length; i++)
            {
                Vector2[] vv = curves[i];
                for (int n = 0; n < vv.Length; n++)
                {
                    Vector2 v = vv[n];
                    for (int m = 0; m < controlX2.Length; m++)
                    {
                        Vector2 local = new Vector2(v.X + controlX2[m], v.Y + controlY2[m]);
                        if (InsideTest(bitmap, local, 255)) return local;
                    }
                }
            }
            throw new InvalidDataException("Cannot found seek");
        }
        /// <summary>
        /// 测试某个点是否在某个封闭图形内部
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="point"></param>
        /// <param name="color"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private bool InsideTest(byte[,] bitmap, Vector2 point, byte color)
        {
            int x = (int)point.X;
            int y = (int)point.Y;
            int length = bitmap.GetLength(0);
            if (x < 0 || y < 0 || x >= length || y >= length)
                return false;
            if (bitmap[y, x] == color) return false;
            int width = bitmap.GetLength(1);
            int left = 0;
            int right = 0;
            for (int l = x - 1; l >= 0; l--)
            {
                if (bitmap[y, l] == color)
                {
                    if (l - 1 >= 0 && bitmap[y, l - 1] == color) continue;
                    left++;
                }
            }
            for (int r = x + 1; r < width; r++)
            {
                if (bitmap[y, r] == color)
                {
                    if (r + 1 < width && bitmap[y, r + 1] == color) continue;
                    right++;
                }
            }
            return (left % 2 != 0) && (right % 2 != 0);
        }
        /// <summary>
        /// 复制数组至输出
        /// </summary>
        /// <param name="srouce"></param>
        /// <param name="column"></param>
        /// <param name="row"></param>
        /// <param name="output"></param>
        /// <param name="unitsPerEm"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void CopySourceToOutput(byte[,] srouce, byte[,] output, uint unitsPerEm, int column, int row)
        {
            int u = (int)unitsPerEm;
            int startY = column * u;
            int startX = row * u;
            for (int y = 0; y < u; y++)
            {
                for(int x = 0; x < u; x++)
                {
                    output[startY + y, startX + x] = srouce[y, x];
                }
            }
        }
        /// <summary>
        /// 保存至图片
        /// </summary>
        /// <param name="finalByte"></param>
        /// <param name="format"></param>
        /// <param name="path"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void SaveToBitmap(byte[,] finalByte, System.Drawing.Imaging.ImageFormat format, string path)
        {
            int length = finalByte.GetLength(0);
            Bitmap bitmap = new Bitmap(length, length);
            for (int y = 0; y < length; y++)
            {
                for (int x = 0; x < length; x++)
                {
                    byte g = finalByte[y, x];
                    bitmap.SetPixel(x, y, Color.FromArgb(255, g, g, g));
                }
            }
            bitmap.Save(path, format);
        }
        /// <summary>
        /// 变换坐标
        /// </summary>
        /// <param name="origin"></param>
        /// <param name="designPerEm"></param>
        /// <param name="uxMin"></param>
        /// <param name="uyMin"></param>
        /// <param name="xDelta"></param>
        /// <param name="yDelta"></param>
        /// <param name="mat"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private Vector2 TransformPoint(Vector2 origin, int designPerEm,
            int uxMin, int uyMin, int xDelta, int yDelta, Matrix3x2 mat)
        {
            var after = Vector2.Transform(origin, mat);
            after.X += (-uxMin + xDelta);
            after.X = after.X >= designPerEm ? designPerEm - 1 : after.X;
            after.Y += (-uyMin + yDelta);
            after.Y = designPerEm - after.Y;
            after.Y = after.Y >= designPerEm ? designPerEm - 1 : after.Y;
            return after;
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private Vector2 TransformPoint(int x0, int y0, float em, int uxMin, int uyMin, float xDelta, float yDelta)
        {
            float x = (x0 - uxMin + xDelta) / em;
            x = x >= 1.0f ? 1.0f : x;
            float y = (em - (y0 - uyMin + yDelta)) / em;
            y = y >= 1.0f ? 1.0f : y;
            return new Vector2(x, y);
        }
        /// <summary>
        /// 读取零号字形
        /// </summary>
        /// <param name="output"></param>
        /// <param name="zero"></param>
        /// <param name="head"></param>
        /// <param name="dPerEm"></param>
        /// <param name="uPerEm"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void ReadGlyphZero(byte[,] output, Glyph zero, HeadTable head, uint dPerEm, uint uPerEm)
        {
            byte[,] localBitmap = new byte[dPerEm, dPerEm];
            Pixel[,] localAssets = new Pixel[dPerEm, dPerEm];
            Pixel[,] localTarget = new Pixel[uPerEm, uPerEm];
            Rasterization(localBitmap, head, zero);
            RasterizationCommand.GenerateSDF(localBitmap, output, localAssets, localTarget, 255);
        }
    }
}
